package com.leo.boot.feature.merge;

import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@UtilityClass
public class MergingHandler {

    private static final Map<Class<?>, MergingDelegate> clazzCache = new ConcurrentHashMap<>();

    private static final Map<Class<?>, List<MergingDelegate>> fieldCache = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static <T extends Mergeable<T>> T merge(T a, T b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }

        Class<T> clazz = (Class<T>) a.getClass();

        MergingDelegate clazzDelegate = clazzCache.computeIfAbsent(clazz, MergingDelegate::initClazzDelegate);
        List<MergingDelegate> fieldDelegates = fieldCache.computeIfAbsent(clazz, MergingDelegate::initFieldDelegates);

        clazzDelegate.pre(a);
        clazzDelegate.pre(b);

        T newInstance = clazz.getDeclaredConstructor().newInstance();
        fieldDelegates.forEach(delegate -> {
            Object aValue = delegate.get(a);
            Object bValue = delegate.get(b);
            aValue = aValue == null ? delegate.nil() : aValue;
            bValue = bValue == null ? delegate.nil() : bValue;
            if (delegate.condition(aValue, bValue)) {
                delegate.set(newInstance, delegate.operate(aValue, bValue));
            }
        });

        clazzDelegate.post(newInstance);
        return newInstance;
    }
}
